所属包:
java.lang;
构造方法:
public Thread();
public Thread(String name);参数是给线程起个名字
public Thread(Runnable target);传入要执行的Runnable对象
public Thread(Runnable target,String name);可以同时传入中间要用“,”隔开
静态方法:
static Thread currentThread();获取当前线程
static void sleep(long millis);具体数字,毫秒值 1000毫秒=1秒
成员变量:
private Runnable target;
成员方法:
void start();//启动线程后,会执行run方法中的代码
void run();线程执行的就是这里的内容
String getName();获取线程的名字
void setName(String name);也是用来给线程起一个名字
State getState();获取线程的状态
调度方式:
抢占式调度:给每个任务分配的时间不等。(可以理解为线程之间进行争抢,谁抢上就是谁执行任务。)
分时调度:给每个任务分配的时间是均等的。(可以在任务完添加一个sleep来控制每个线程执行的时间。)
进程和线程:
进程:
一个应用程序就是一个或者多个进程。
线程:
一个进程有一个或者多个线程。
public class Test01 {
public static void main(String[] args) {
while(true);
}
}
获取线程的名字:Thread.currentThread();+ ?.getName();
public class Test01 {
public static void main(String[] args) {
//获取当前线程,执行当前代码的线程
Thread currentThread = Thread.currentThread();
//获取线程的名字
String name = currentThread.getName();
//打印线程的名字
System.out.println(name);
}
}
Runnable接口:
public abstract void run();
Runnable接口的方式和继承Thread类的方式:建议使用Runnable方式
a.线程和任务分离,解耦合,提高代码的健壮性。
b.避免了Java单继承的局限性
c.线程池里面,只能传入Runnable或者Callable类型的对象,不用new Thread
每一个线程启动后都会有一个栈,各自在各自的栈中执行任务。
线程的开销比一般对象的开销要大。
运行一个线程:
第一种方法:
1.先创建一个继承了Thread的的类,并重写run方法(线程执行的任务就是run方法中的代码);
public class SubThread extends Thread{
@Override
public void run() {
//获取当前线程,执行当前代码的线程
Thread t = Thread.currentThread();
//获取线程的名称
String name = t.getName();
//System.out.println(name+" hello Thread");
for (int i = 0; i < 100; i++) {
System.out.println(name+"--"+i);
try {
//目的是让线程执行完任务睡一会儿,执行速度慢一些,方便我们看效果
Thread.sleep(100);//单位是毫秒
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
2.在测试类中创建该线程对象,并开始任务(?.start();)
public class Test02 {
public static void main(String[] args) {
//创建一个子线程
Thread t = new SubThread();
//开始任务
t.start();
}
}
注意:run()与start()的区别:
public class Test02 {
public static void main(String[] args) {
//创建一个子线程
Thread t = new SubThread();
//t.start();
//如果直接调用run方法,run方法中的代码是由主线程执行的
t.run();
}
}
直接调用run()方法,是主线程main去执行的,没有显示出我们子线程的作用,没有达到我们写线程的目的。
第二种方法:
使用匿名内部类创建Runnable对象;
public static void main(String[] args) {
Runnable a = new Runnable(){
@Override
public void run() {
Thread x = Thread.currentThread();
String name = x.getName();
for(int i =0;i<100;i++) {
System.out.println(name+i);
}
}
};
Thread b = new Thread(a,"线程一");
b.start();
}
}
线程的并行和并发:
并行(必须是多核CPU在可以达到): 一个或者多个事件(任务)在同一时间点(同时)执行,好比你可以一般打游戏一边吃饭,同事进行。
并发(): 一个或者多个事件在同一时间段(先后)执行,你有两碗饭,你只有把嘴里的解决了才能吃下一口。
多线程:
顾名思义是多个线程同时运行
public static void main(String[] args) {
Runnable a = new Runnable(){
@Override
public void run() {
Thread x = Thread.currentThread();
String name = x.getName();
for(int i =0;i<100;i++) {
System.out.println(name+i);
}
}
};
Thread b = new Thread(a,"线程一");
b.start();
new Thread(a,"线程二").start();
}
}
多线程注意:
多个线程执行的时候:谁线程值不确定,谁执行多长时间不确定。
线程的安全:
public static void main(String[] args) throws InterruptedException {
//创建一个集合,使其可以存入20000个元素
ArrayList<Integer> list = new ArrayList<>(20000);
//通过匿名内部类的方式创建Runnable类型的对象
Runnable r = new Runnable() {
@Override
public void run() {
for (int i = 0; i < 10000; i++) {
list.add(i);
}
//用来判断是否两个线程都执行任务了
System.out.println("=================");
}
};
//用匿名对象的形式创建两个线程去执行任务
new Thread(r).start();
new Thread(r).start();
//用来限制主线程main,防止线程还没执行完,主线程已经执行了sout;
Thread.sleep(3000);
System.out.println(list.size());
}
第一次运行:
第二次运行:
正常下应该是存储20000个,现在会发生丢失问题。
为什么会发生丢失问题?线程安全产生的前提:多个线程访问同一资源(数据)
线程的执行无非就是这三步;
之所以会发生错误就是线程一拿到数字了,进行第二部+1了,但是还没有执行第三步,任务被线程二抢去了,所以没存上。再等到线程一抢回来,继续存,两个数存了同一个数,就相当于丢了。
static int n = 0;
public static void main(String[] args) throws InterruptedException {
Runnable r = new Runnable() {
@Override
public void run() {
String name = Thread.currentThread().getName();
for (int i = 0; i < 100; i++) {
n++;
System.out.println(name+n);
}
}
};
new Thread(r,"天线宝宝").start();
new Thread(r,"花园宝宝").start();
Thread.sleep(3000);
System.out.println(n);
}
}
如何解决线程丢失(上锁):整个的操作不是原子操作:
在线程一进行操作时,另一个线程二进行等待,只有线程一操作完,释放锁对象,线程二才能抢。
1.使用synchronized代码块
语法:
synchronized(锁对象){//可以是任意类型的对象
//写有可能发生线程安全问题的代码
}
static int n = 0;
public static void main(String[] args) throws InterruptedException {
Runnable r = new Runnable() {
@Override
public void run() {
for (int i = 0; i < 10000; i++) {
synchronized (this) {
n++;
}
}
}
};
new Thread(r).start();
new Thread(r).start();
Thread.sleep(3000);
System.out.println(n);
}
2.使用synchronized方法
静态synchronized方法:在static和返回值之间加synchronized关键字
非静态synchronized方法:在返回值之前加synchronized关键字
synchronized代码块和方法:代码执行完毕后,自动释放锁
public class Testhah {
static int n = 0;
public static void main(String[] args) throws InterruptedException {
Runnable r = new Runnable() {
@Override
public void run() {
for (int i = 0; i < 10000; i++) {
//调用synchronized方法
add();
}
}
};
new Thread(r).start();
new Thread(r).start();
Thread.sleep(3000);
System.out.println(n);
}
public static synchronized void add() {
n++;
}
}
3.使用Lock锁
Lock接口:
void lock();//上锁
void unlock();//开锁
常用实现类:ReentrantLock
构造方法:
public ReentrantLock();
public class lulalei {
static int n = 0;
public static void main(String[] args) throws InterruptedException {
Lock lock = new ReentrantLock();
Runnable r = new Runnable() {
@Override
public void run() {
for (int i = 0; i < 10000; i++) {
//上锁
lock.lock();
n++;
//解锁
lock.unlock();
}
}
};
new Thread(r).start();
new Thread(r).start();
Thread.sleep(3000);
System.out.println(n);
}
}
线程的状态:
NEW:新建状态。创建了一个线程,启动之前处于该状态
public class Test01 {
public static void main(String[] args) {
Thread t = new Thread();
State s = t.getState();
System.out.println(s);//NEW
}
}
RUNNABLE:可运行状态
public class Test03 {
public static void main(String[] args) throws InterruptedException {
Runnable r = new Runnable() {
@Override
public void run() {
for(;;);
}
};
Thread t = new Thread(r);
t.start();
Thread.sleep(100);//100毫秒保证子线程开始执行Thread.sleep(1000000);这一行代码了
//获取并打印线程的状态
System.out.println(t.getState());
}
}
BLOCKED:阻塞状态
public class Test05 {
public static void main(String[] args) throws InterruptedException {
Runnable r = new Runnable() {
@Override
public void run() {
synchronized(this) {
for(;;);
}
}
};
Thread t1 = new Thread(r);
t1.start();
Thread t2 = new Thread(r);
t2.start();
Thread.sleep(100);//100毫秒保证两个子线程已经启动,并开始执行代码了
//获取并打印线程的状态
System.out.println(t1.getState());
System.out.println(t2.getState());
//获取了锁对象开始执行for循环的线程处于RUNNABLE状态
//没有获取锁对象的线程处于BLOCKED状态。
}
}
WAITING:无限等待状态
public class Test06 {
public static void main(String[] args) throws InterruptedException {
Lock lock = new ReentrantLock();
Runnable r = new Runnable() {
@Override
public void run() {
lock.lock();
for(;;);
}
};
Thread t1 = new Thread(r);
t1.start();
Thread t2 = new Thread(r);
t2.start();
Thread.sleep(100);//100毫秒保证两个子线程已经启动,并开始执行代码了
//获取并打印线程的状态
System.out.println(t1.getState());
System.out.println(t2.getState());
//获取了锁对象开始执行for循环的线程处于RUNNABLE状态
//没有获取Lock锁对象的线程处于WAITING状态。
}
}
TIMED_WAITING:计时等待状态
public class Test04 {
public static void main(String[] args) throws InterruptedException {
Runnable r = new Runnable() {
@Override
public void run() {
try {
Thread.sleep(1000000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
Thread t = new Thread(r);
t.start();
Thread.sleep(100);//100毫秒保证子线程开始执行Thread.sleep(1000000);这一行代码了
//获取并打印线程的状态
System.out.println(t.getState());
}
}
TERMINATED:消亡状态
public class Test02 {
public static void main(String[] args) throws InterruptedException {
Runnable r = new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
};
Thread t = new Thread(r);
t.start();
Thread.sleep(1000);//1000毫秒子线程一定把任务执行完毕了
//获取并打印线程的状态
System.out.println(t.getState());
}
}
线程的通信:线程和线程之间的沟通
等待唤醒机制:
Object类:
void wait();//就会让线程进入等待状态。WAITING状态
void wait(long time);//调用该方法,会让线程进入计时等待状态。TIMED_WAITING
void notify();//调用该方法,会让线程醒来,接着执行任务。
void notifyAll();//调用该方法,会唤醒当前锁对象上等待的所有线程
注意事项:
1.这些方法都必须写在synchronized代码块或者synchronized方法中
2.调用这些方法的对象,必须和锁对象一致。
3. t.notify方法,只能唤醒t锁对象上等待的线程
4. 调用了wait(不论是否有参数)方法后,会自动释放锁对象。
例子:
1.创建一个Fruit类
public class Fruit {
private int stock;
public int getStock() {
return stock;
}
public void setStock(int stock) {
this.stock = stock;
}
}
2.官网线程类
public class NetShop implements Runnable{
private Fruit f;
public NetShop(Fruit f) {
super();
this.f = f;
}
@Override
public void run() {
while(true) {
synchronized(f) {
//判断是否退出循环
if(f.getStock()<=0) {
break;
}
//偶数份,官网卖
if(f.getStock()%2==0) {
//每卖一份,库存减1
f.setStock(f.getStock()-1);
System.out.println("官网正在卖出第"+(100-f.getStock())+"份,还剩余"+f.getStock()+"份");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
//唤醒当前锁对象上等待的线程
f.notify();
}else {
//奇数份,官网进入等待状态
try {
f.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
}
3.实体店线程类
public class FrontShop implements Runnable{
private Fruit f;
public FrontShop(Fruit f) {
super();
this.f = f;
}
@Override
public void run() {
while(true) {
synchronized(f) {
//判断是否退出循环
if(f.getStock()<=0) {
break;
}
//奇数份,实体店卖
if(f.getStock()%2==1) {
//每卖一份,库存减1
f.setStock(f.getStock()-1);
System.out.println("实体店正在卖出第"+(100-f.getStock())+"份,还剩余"+f.getStock()+"份");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
//唤醒当前锁对象上等待的线程
f.notify();
}else {
//偶数份,实体店进入等待状态
try {
f.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
}
4.测试类
public class Test01 {
public static void main(String[] args) {
//创建Fruit类型的对象
Fruit f = new Fruit();
f.setStock(100);
//创建官网和实体店类型的对象
Runnable r1 = new NetShop(f);
Runnable r2 = new FrontShop(f);
//创建两个线程,分别指向这两个任务
new Thread(r1).start();
new Thread(r2).start();
}
}
注意:
我们做这个题要明确两点:
1.这两边需要一个共同的锁对象
2.根据库存去判断谁该执行了,谁该等待了
线程池:
简单来说就是一个存放线程的池子,线程执行完任务会回到池子里面等待下一个任务的到来,提高效率。
1.提高响应速度。预先创建好了线程,只等任务过来执行。
2.降低资源消耗。线程池中的线程,执行完任务后,又返回到线程池中,下一个任务到来后可以继续使用该线程。
3.提高线程的可管理性。一个线程大约需要消耗1M的空间,线程池可以设置最大线程的数量。
Executors类:
static ExecutorService newFiexdThredadPool(int nThread);
ExecutorService接口:
void execute(Runnable r);//执行任务
<T> Future<T> submit(Callable<T> c);//执行任务
Future<?> submit(Runnable r);//执行任务
void shutdown();//关闭线程池
public class Test01 {
public static void main(String[] args) {
Runnable r = new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
};
//创建一个固定线程池对象
ExecutorService es = Executors.newFixedThreadPool(2);
//执行任务
es.execute(r);
//关闭线程池
es.shutdown();
}
}
下面是如果有多个任务线程池怎么运行:
public class Test02 {
public static void main(String[] args) {
Runnable r = new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
//创建一个固定线程池对象
ExecutorService es = Executors.newFixedThreadPool(2);
//执行任务
es.execute(r);
es.execute(r);
es.execute(r);
//关闭线程池
es.shutdown();
}
}
从结果,可以看出来,先执行任务的那个把任务执行完,接着把第三次任务执行了,后执行任务的,执行完任务就回到了线程池。
Future<T>接口
T get();//必须等子线程把任务执行完成,return以后才可以获取返回的结果。
Callable<T>接口(与Runnable很像):
T call();
public class Test04 {
public static void main(String[] args) throws InterruptedException, ExecutionException {
Runnable r = new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
};
//创建一个固定线程池对象
ExecutorService es = Executors.newFixedThreadPool(2);
Future<?> future = es.submit(r);
Object result = future.get();//null
System.out.println(result);
//关闭线程池
es.shutdown();
}
}
可以看出来Runnable没有返回值,所以就不用接收了。
public class Test03 {
public static void main(String[] args) throws InterruptedException, ExecutionException {
Callable<String> c = new Callable<String>() {
@Override
public String call() throws Exception {
//call方法中的代码是某个子线程执行的
System.out.println(Thread.currentThread().getName());
Thread.sleep(5000);
return "test callable";
}
};
//创建一个固定线程池对象
ExecutorService es = Executors.newFixedThreadPool(2);
Future<String> future = es.submit(c);
//这行代码是主线程执行的
String result = future.get();
System.out.println(result);
//关闭线程池
es.shutdown();
}
}