1.2 什么是进程
目前操作系统都是支持多进程,可以同时执行多个进程,通过进程ID区分
1.3 什么是线程
线程的组成
线程的特点
1.4 进程和线程的区别B、创建线程的三种方式2.1 继承Thread类重写run()方法
具体实现
继承类
public class MyThread extends Thread { @Override public void run() { for (int i = 1; i " + i); } }}
测试类
public class TestThread { public static void main(String[] args) { MyThread myThread = new MyThread(); myThread.start(); for (int i = 1; i "+i); } }}
结果
获取线程ID和名称
代码
public class TestThread { public static void main(String[] args) { MyThread t=new MyThread(); t.start(); //只能在继承Thread类的情况下用 System.out.println("线程id:"+t.getId()); System.out.println("线程名字:"+t.getName()); //调用Thread类的静态方法获取当前线程(这里获取的是主线程) System.out.println("线程id:"+Thread.currentThread().getId()); System.out.println("线程名字:"+Thread.currentThread().getName()); }}
修改线程名称
代码
public class MyThread extends Thread{ public MyThread() {}//无参构造器 public MyThread(String name) { super(name); } public void run() { for(int i=1;i<=50;i++) {} }}
public class TestThread { public static void main(String[] args) { MyThread t1=new MyThread("子线程1");//通过构造方法 MyThread t2=new MyThread(); t2.setName("子线程2"); System.out.println("线程t1的id:"+t1.getId()+" 名称:"+t1.getName()); System.out.println("线程t2的id:"+t2.getId()+" 名称:"+t2.getName()); }}
2.2 实现Runnable接口实现run()方法
具体实现
实现接口
public class MyRunnable implements Runnable{//实现接口 @Override public void run() {//实现run方法 // TODO Auto-generated method stub for(int i=1;i<=10;i++) { System.out.println("子线程:"+i); } }}
测试类
public class TestRunnable { public static void main(String[] args) { //1.创建MyRunnable对象,表示线程执行的功能 Runnable runnable=new MyRunnable(); //2.创建线程对象 Thread th=new Thread(runnable); //3.启动线程 th.start(); for(int i=1;i<=10;i++) { System.out.println("主线程:"+i); } }}
使用匿名内部类
public class TestRunnable { public static void main(String[] args) { Runnable runnable=new Runnable() { @Override public void run() { // TODO Auto-generated method stub for(int i=1;i<=10;i++) { System.out.println("子线程:"+ i); } } }; Thread th=new Thread(runnable); th.start(); }}
2.3 实现Callable接口
Callable和Thread、Runnable比较
具体实现
实现接口
import java.util.Random;import java.util.concurrent.Callable;import java.util.concurrent.ExecutionException;import java.util.concurrent.FutureTask;public class TestCallable implements Callable{ @Override public Integer call() throws Exception { // TODO Auto-generated method stub return new Random().nextInt(10); }}
测试类
import java.util.concurrent.ExecutionException;import java.util.concurrent.FutureTask;public class Test { public static void main(String[] args) throws InterruptedException, ExecutionException { TestCallable tc=new TestCallable(); FutureTask ft=new FutureTask(tc); //创建线程对象 Thread th=new Thread(ft); th.start(); //获取线程得到的返回值 Integer In=ft.get(); System.out.println(In); }}
C、线程的状态3.1 基本四状态
3.2 等待状态
3.3 阻塞状态
D、线程常用的方法4.1 线程休眠(sleep)
子线程
public class SleepThread extends Thread{ @Override public void run() { for (int i = 1; i <= 10; i++) { System.out.println(Thread.currentThread().getName()+":"+i); try {Thread.sleep(100); } catch (InterruptedException e) {e.printStackTrace(); } } }}
PS:sleep()的异常在run方法中是不能抛出的,只能用try–catch处理
测试类
public class Test01 { public static void main(String[] args) { SleepThread sleepThread = new SleepThread(); sleepThread.start(); }}
结果:每次间隔100ms输出一次
4.2 线程放弃(yield)
子线程
public class YieldThread extends Thread{ @Override public void run() { for (int i=1;i<=10;i++){ System.out.println(Thread.currentThread().getName()+":"+i); Thread.yield();//主动放弃资源 } }}
测试类
public class Test01 { public static void main(String[] args) { YieldThread yieldThread01 = new YieldThread(); YieldThread yieldThread02 = new YieldThread(); yieldThread01.start(); yieldThread02.start(); }}
结果:基本都会交替进行,也会有一个线程连输出
4.3 线程加入(join)
子线程
public class JoinThread extends Thread{ @Override public void run() { for (int i=1;i<=10;i++){ System.out.println(Thread.currentThread().getName()+":"+i); } }}
测试类
public class Test01 { public static void main(String[] args) throws InterruptedException { for (int i=1;i<=10;i++){ System.out.println(Thread.currentThread().getName()+":"+i); if(i==5){JoinThread joinThread = new JoinThread();joinThread.start();joinThread.join(); } } }}
结果:当主线程打印到5的时候,这时候子线程加入进来,就先执行完子线程,在执行主线程
4.4 守护线程(setDaemon)
子线程
public class TestThread extends Thread{ @Override public void run() { for(int i=1;i<=1000;i++){ System.out.println(Thread.currentThread().getName()+":"+i); try {Thread.sleep(50); } catch (InterruptedException e) {e.printStackTrace(); } } }}
测试类
public class Test01 { public static void main(String[] args) throws InterruptedException { TestThread daemonThread = new TestThread(); daemonThread.setDaemon(true);//设置守护线程 daemonThread.start(); for (int i=1;i<=10;i++){ System.out.println(Thread.currentThread().getName()+":"+i); Thread.sleep(100); } }}
结果:当主线程结束时,子线程也跟着结束,并不会继续执行下去打印输出
4.5 线程优先级(setPriority)
子线程
public class TestThread extends Thread{ @Override public void run() { for(int i=1;i<=100;i++){ System.out.println(Thread.currentThread().getName()+":"+i); } }}
测试
public class Test01 { public static void main(String[] args) throws InterruptedException { TestThread th1 = new TestThread(); TestThread th2 = new TestThread(); TestThread th3 = new TestThread(); th1.setPriority(10);//设置线程1优先级10 th1.start(); th2.start();//线程2优先级默认不变,为5 th3.setPriority(1);//设置线程3优先级为1 th3.start(); }}
结果:优先级(th1>th2>th3)线程3应该在最后打印
E、线程安全问题5.1 卖票案例
需求:模拟三个窗口,每个窗口有100个人,同时抢10张票
使用继承Runnable接口的方法
public class BuyTicketRunnable implements Runnable{ private int ticketNum=10; @Override public void run() { for(int i=1;i<=100;i++) { if(ticketNum<=0) break; System.out.println("在"+Thread.currentThread().getName()+"买到了第"+ticketNum+"张票!"); ticketNum--; } }}
测试方法
public class BuyTicketTest { public static void main(String[] args) { // TODO Auto-generated method stub Runnable runnable=new BuyTicketRunnable(); Thread th1=new Thread(runnable,"窗口1"); Thread th2=new Thread(runnable,"窗口2"); Thread th3=new Thread(runnable,"窗口3"); th1.start(); th2.start(); th3.start(); }}
结果
5.2 同步代码块
对卖票案例改进
public class BuyTicketRunnable implements Runnable{ static Object obj=new Object(); private int ticketNum=10; @Override public void run() { for(int i=1;i<100;i++) { //把具有安全隐患的代码锁住即可,如果锁多了就会效率低 synchronized (obj) {//锁必须多个线程用的是同一把锁!!也可以使用this,表示的是该对象本身 System.out.println("在"+Thread.currentThread().getName()+"买到了第"+ticketNum+"张票!"); ticketNum--; } } }}
5.3 同步方法
买票案例改进
public class BuyTicketRunnable implements Runnable{ private int ticketNum=10; @Override public void run() { for(int i=1;i0) { System.out.println("在"+Thread.currentThread().getName()+"买到了第"+ticketNum+"张票!"); ticketNum--; } }}
5.4 Lock锁
对买票案例改进
import java.util.concurrent.locks.Lock;import java.util.concurrent.locks.ReentrantLock;public class BuyTicketRunnable implements Runnable{ private int ticketNum=10; Lock lock=new ReentrantLock();//接口=实现类 可以使用不同的实现类 @Override public void run() { for(int i=1;i0) { System.out.println("在"+Thread.currentThread().getName()+"买到了第"+ticketNum+"张票!"); ticketNum--; } }catch(Exception e) { e.printStackTrace(); }finally { //关闭锁:--->即使有异常,这个锁也可以得到释放 lock.unlock(); } } }}
5.5 线程死锁
*案例:男孩女孩一起去吃饭,但是桌子上只有两根筷子,如果两个人同时抢到一根筷子而不放弃,这样两个人都吃不上饭,这样就形成死锁了;必须要有一个人放弃争抢,等待另一个人用完,释放资源,这个人之后才会获得两根筷子,两个人才能都吃上饭 *
package 多线程;class Eat{ //代表两个筷子 public static Object o1=new Object(); public static Object o2=new Object(); public static void eat() { System.out.println("可以吃饭了"); }}class BoyThread extends Thread{ public void run() { synchronized (Eat.o1) { System.out.println("男孩拿到了第一根筷子!"); synchronized (Eat.o2) { System.out.println("男孩拿到了第二根筷子!"); Eat.eat(); } } }}class GirlThread extends Thread{ public void run() { synchronized (Eat.o2) { System.out.println("女孩拿到了第二根筷子!"); synchronized (Eat.o1) { System.out.println("女孩拿到了第一根筷子!"); Eat.eat(); } } }}public class MyLock { public static void main(String[] args) { BoyThread boy=new BoyThread(); GirlThread girl=new GirlThread(); boy.start(); girl.start(); }}
结果
解决办法
public class MyLock { public static void main(String[] args) { BoyThread boy=new BoyThread(); GirlThread girl=new GirlThread(); boy.start(); try { Thread.sleep(100); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } girl.start(); }}
在写程序中要避免这种死锁:减少同步资源的定义,避免嵌套同步
F、线程通信问题6.1 wait()和wait(long timeout)
sleep和wait的区别:sleep进入阻塞状态没有释放锁,wait进入阻塞状态但是同时释放了锁
6.2 notify()和notifyAll()6.3 生产者和消费者问题
功能分解一:商品类
public class Product {//商品类 private String name;//名字 private String brand;//品牌 boolean flag = false;//设置标记,false表示商品没有,等待生产者生产 public synchronized void setProduct(String name, String brand) {//生产商品,同步方法,锁住的是this if (flag == true) {//如果flag为true,代表有商品,不生产,等待消费者消费 try {wait(); } catch (InterruptedException e) {e.printStackTrace(); } } //生产商品 this.setName(name); this.setBrand(brand); System.out.println("生产者生产了" +this.getBrand() +this.getName()); //生产完,设置标志 flag = true; //唤醒消费线程 notify(); } public synchronized void getProduct() { if (flag == false) {//如果是false,则没有商品,等待生产者生产 try {wait(); } catch (InterruptedException e) {e.printStackTrace(); } } //如果有商品,消费 System.out.println("消费者消费了" + this.getBrand() +this.getName()); //设置标志 flag = false; //唤醒线程 notify(); } public void setName(String name) { this.name = name; } public String getName() { return name; } public void setBrand(String brand) { this.brand = brand; } public String getBrand() { return brand; }}
功能分解二:生产者线程
public class ProducterThread extends Thread {//生产者线程 private Product p; public ProducterThread(Product p) { this.p = p; } @Override public void run() { for (int i = 1; i <= 10; i++) { if(i%2==0){//如果是奇数,就生产巧克力,如果是偶数,就生产方便面p.setProduct("巧克力","德芙"); }else{p.setProduct("方便面","康师傅"); } } }}
功能分解三:消费者线程
public class CustomerThread extends Thread {//消费者线程 private Product pro; public CustomerThread(Product pro) { this.pro = pro; } @Override public void run() { for (int i = 1; i <= 10; i++) { pro.getProduct(); } }}
功能分解四:测试类
public class Test { public static void main(String[] args) { Product p = new Product(); ProducterThread pth = new ProducterThread(p); CustomerThread cth = new CustomerThread(p); pth.start(); cth.start(); }}
结果:生产者生产一件商品,消费者消费一件商品,交替进行
《java视频教学》