JAVA–线程(Thread)

  • Post author:
  • Post category:java


所属包:

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();
			}
		}
	}
}

try{}catch()是解决异常,详情可见



https://so.csdn.net/so/search?spm=1001.2101.3001.7498&q=java%E5%BC%82%E5%B8%B8%E5%A4%84%E7%90%86&t=&u=&utm_term=java%E5%BC%82%E5%B8%B8%E5%A4%84%E7%90%86&utm_medium=distribute.pc_toolbar_associateword.none-task-associate_word-opensearch_query-4-java%3Cem%3E%E5%BC%82%E5%B8%B8%3C%2Fem%3E%E5%A4%84%E7%90%86-null-null.nonecase&depth_1-utm_source=distribute.pc_toolbar_associateword.none-task-associate_word-opensearch_query-4-java%3Cem%3E%E5%BC%82%E5%B8%B8%3C%2Fem%3E%E5%A4%84%E7%90%86-null-null.nonecase&request_id=165423633116781685375078&opensearch_request_id=165423633116781685375078



https://so.csdn.net/so/search?spm=1001.2101.3001.7498&q=java%E5%BC%82%E5%B8%B8%E5%A4%84%E7%90%86&t=&u=&utm_term=java%E5%BC%82%E5%B8%B8%E5%A4%84%E7%90%86&utm_medium=distribute.pc_toolbar_associateword.none-task-associate_word-opensearch_query-4-java%3Cem%3E%E5%BC%82%E5%B8%B8%3C%2Fem%3E%E5%A4%84%E7%90%86-null-null.nonecase&depth_1-utm_source=distribute.pc_toolbar_associateword.none-task-associate_word-opensearch_query-4-java%3Cem%3E%E5%BC%82%E5%B8%B8%3C%2Fem%3E%E5%A4%84%E7%90%86-null-null.nonecase&request_id=165423633116781685375078&opensearch_request_id=165423633116781685375078


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();
	}
}



版权声明:本文为zailalaniubi原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。