==多线程的建立方法有:四种!!! #F44336==java
先上代码:面试
/** * 建立多线程的方法一: * 建立继承Thread的子类 * * @author 🏹☂࿈秋鹜࿈🏹️ * @create 2020/3/7 18:41 */ //线程类 class NumCount extends Thread{ //run方法里是要执行的代码 @Override public void run() { //输出0-99 for (int i = 0; i < 100; i++) { System.out.println(NumCount.currentThread().getName()+":"+i); } } } //主类 public class MyThread { public static void main(String[] args) { //建立Thread子类的对象 NumCount nc1 = new NumCount(); //给线程起个名字 nc1.setName("计数线程1"); //开启线程 nc1.start(); } }
注意事项windows
先上代码:安全
/** * 建立多线程的方法二: * 实现Runnable接口 * * @author 🏹☂࿈秋鹜࿈🏹️ * @create 2020/3/7 18:41 */ //线程类 class NumCount implements Runnable{ //run方法里是要执行的代码 @Override public void run() { //输出0-99 for (int i = 0; i < 100; i++) { System.out.println(Thread.currentThread().getName()+":"+i); } } } //主类 public class MyThread { public static void main(String[] args) { //实例化Runnable接口 NumCount numberCount = new NumCount(); //实例化一个线程,构造方法的参数为实例化的接口 Thread nc1 = new Thread(numberCount); //给线程起个名字 nc1.setName("计数线程1"); //开启线程 nc1.start(); } }
注意事项多线程
先上代码:ide
/** * 建立多线程的方法三: * 实现Callable接口 --- JDK5.0新增 * * @author 🏹☂࿈秋鹜࿈🏹️ * @create 2020/3/7 18:41 */ //线程类 class NumCount implements Callable<Integer> { //重写call()方法 @Override public Integer call() throws Exception { //计算1-100的数的和 int sum = 0; for (int i = 1; i <= 100; i++) { System.out.println(Thread.currentThread().getName() + ":" + i); sum += i; } return sum; } } //主类 public class MyThread { public static void main(String[] args) throws ExecutionException, InterruptedException { //建立Callable接口实现类的对象 NumCount numCount = new NumCount(); //将Callable接口实现类的对象传递到FutureTask构造器,建立FutureTask对象 FutureTask<Integer> futureTask = new FutureTask<>(numCount); //建立线程,传递FutureTask对象到Thread构造器中 Thread nc1 = new Thread(futureTask); nc1.setName("计数线程1"); nc1.start(); //获取Callable中call方法的返回值 Integer sum = futureTask.get(); System.out.println("总和为:" + sum); } }
注意事项this
先上代码:线程
/** * 建立多线程的方法四: * 建立线程池 * * @author 🏹☂࿈秋鹜࿈🏹️ * @create 2020/3/7 18:41 */ //线程类 class NumCount implements Runnable { //重写run方法 @Override public void run() { for (int i = 0; i < 100; i++) { System.out.println(Thread.currentThread().getName() + ":" + i); } } } //主类 public class MyThread { public static void main(String[] args) throws ExecutionException, InterruptedException { //建立指定线程数量的线程池 ExecutorService service = Executors.newFixedThreadPool(10); //执行指定的线程的操做,须要提供实现Runnable接口或Callable接口实现类的对象 service.execute(new NumCount());//用于Runnable // service.submit();//用于Callable //关闭线程池 service.shutdown(); } }
注意事项设计
Q: 为何会重票错票?
A: 当出现极限状况:当多个线程同时调用共享数据,而且线程还未结束,就会出现重复调取同一个值的状况(重票),票数不知足线程数时,会出现负数(错票)。code
#### 解决方案:线程同步机制
线程同步机制:
当某一个线程在使用共用的数据(执行被同步的代码)时,其余线程要进行等候,不管这个线程是否处于阻塞状态。
若是不明白的话,就想象一下旅游景点女厕所门口排队的女性朋友们。
#### 具体办法:
1. 同步代码块
这个方法涉及到synchronized修饰词。
基本格式:
synchronized(同步监视器(锁)){ * //要被同步的代码 * }
说明:
使用示例:
/** * 用同步代码块解决线程安全问题 * * 问题:三个窗口卖100张票,用线程解决。 * * * @author 🏹☂࿈秋鹜࿈🏹️ * @create 2020/3/5 17:50 */ class TicketTread extends Thread{ //保证多个线程共用一份数据,须要设置成静态的 public static int ticket = 100; //同一把锁 public static Object object = new Object(); @Override public void run() { while (true){ //同步代码块 synchronized (object){ if (ticket>0){ try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(TicketTread.currentThread().getName() + ":正在售卖第" + ticket + "张票"); ticket--; }else{ break; } } } } } public class TicketDemo { public static void main(String[] args) { TicketTread t1 = new TicketTread(); TicketTread t2 = new TicketTread(); TicketTread t3 = new TicketTread(); t1.setName("窗口一"); t2.setName("窗口二"); t3.setName("窗口三"); t1.start(); t2.start(); t3.start(); } }
2. 同步方法
这个方法涉及到synchronized修饰词。
基本格式:
synchronized 数据类型 方法名(){ //须要被同步的代码 }
注意:
使用示例:
class Windows1 implements Runnable{ //这里无需设定静态变量,由于多个线程调用同一个接口 public int ticket = 100; Object object = new Object(); @Override public void run() { while (true){ show(); } } //同步方法 public synchronized void show(){ if (ticket>0){ try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(TicketTread.currentThread().getName() + ":正在售卖第" + ticket + "张票"); ticket--; } } } public class TicketDemo2 { public static void main(String[] args) { Windows1 windows = new Windows1(); Thread t1 = new Thread(windows); Thread t2 = new Thread(windows); Thread t3 = new Thread(windows); t1.setName("窗口一"); t2.setName("窗口二"); t3.setName("窗口三"); t1.start(); t2.start(); t3.start(); } }
3. ReentrantLock
jdk1.5新特性
用法和同步代码块相似。用调用方法的办法替代代码块。
使用示例:
class Windows implements Runnable{ public static int ticket = 100; private ReentrantLock lock = new ReentrantLock(); @Override public void run() { while (true){ try { //调用锁定的方法 lock.lock(); //lock以后的代码至关于同步代码块的效果 if (ticket > 0) { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + ":正在售卖第" + ticket + "张票"); ticket--; } else { break; } } finally { //调用解锁的方法 lock.unlock(); } } } } public class LockTest { public static void main(String[] args) { Windows windows = new Windows(); Thread t1 = new Thread(windows); Thread t2 = new Thread(windows); Thread t3 = new Thread(windows); t1.setName("窗口一"); t2.setName("窗口二"); t3.setName("窗口三"); t1.start(); t2.start(); t3.start(); } }
面试题:synchronized 和 Lock 的区别?
两个线程打印1-100,交替打印
问题代码:
/** * 两个线程打印1-100,交替打印 * * @author 🏹☂࿈秋鹜࿈🏹️ * @create 2020/3/6 22:41 */ class Number implements Runnable{ private int number = 1; @Override public void run() { while (true){ synchronized (this) { if (number<=100){ try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + ":数字:" + number); number++; }else { break; } } } } } public class Communicate { public static void main(String[] args) { Number number = new Number(); Thread t1 = new Thread(number); Thread t2 = new Thread(number); t1.setName("线程1"); t2.setName("线程2"); t1.start(); t2.start(); } }
当你运行此段代码时,会出现仅单一线程运行的状况
t1线程一致占用锁,没法进行交替打印
须要利用三剑客:wait(),notify(),notifyAll()
说明:
/** * 两个线程打印1-100,交替打印 * * @author 🏹☂࿈秋鹜࿈🏹️ * @create 2020/3/6 22:41 */ class Number implements Runnable{ private int number = 1; @Override public void run() { while (true){ synchronized (this) { //使用notify()方法唤醒线程 // this.notify(); notify(); if (number<=100){ try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + ":数字:" + number); number++; try { //使用wait()方法使得线程处于阻塞状态 // this.wait(); wait(); } catch (InterruptedException e) { e.printStackTrace(); } }else { break; } } } } } public class Communicate { public static void main(String[] args) { Number number = new Number(); Thread t1 = new Thread(number); Thread t2 = new Thread(number); t1.setName("线程1"); t2.setName("线程2"); t1.start(); t2.start(); } }
面试题:sleep() 和 wait() 的异同?
问题解决:
/** * 用线程同步的方法解决单例模式的线程安全问题 * * @author 🏹☂࿈秋鹜࿈🏹️ * @create 2020/3/6 16:32 */ public class BankDemo { } class Bank{ //单例模式(private构造方法,确保该类使用对象惟一) private Bank(){}; public static Bank instence = null; //此处可能引起线程安全问题 public static Bank getInstence(){ //方式一,效率稍差(线程所有同步) // synchronized (Bank.class) { // if (instence==null){ // instence = new Bank(); // } // return instence; // } //方式二:效率较高(一小部分线程同步) if (instence==null){ synchronized (Bank.class) { if (instence==null){ instence = new Bank(); } } } return instence; } }
问题描述:
银行有一个帐户,有两个储户分别向同一个帐户存3000元,每次存1000,存3次,每次存完打印帐余额。
问题解决:
/** * @author 🏹☂࿈秋鹜࿈🏹️ * @create 2020/3/6 22:19 */ //帐户(共享数据) class Account{ private double balance; public Account(double balance){ this.balance =balance; } //存钱 public synchronized void deposit(double amt){ if (amt > 0){ balance += amt; System.out.println(Thread.currentThread().getName() + ":存钱成功!当前余额为:" + balance); } } } //储户(线程) class Customer implements Runnable{ private Account acct; //初始化数据 public Customer(Account acct){ this.acct = acct; } @Override public void run() { for (int i = 0; i < 3; i++) { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } acct.deposit(1000); } } } public class AccountTest { public static void main(String[] args) { Account acct = new Account(0); Customer customer = new Customer(acct); Thread c1 = new Thread(customer); Thread c2 = new Thread(customer); c1.setName("甲"); c2.setName("乙"); c1.start(); c2.start(); } }
问题描述:
生产者(Productor)将产品交给店员(Clerk),而消费者(Customer)从店员处取走产品。店员一次只能持有固定数量的产品(好比:20),若是生产者试图生产更多的产品,店员会叫生产者停一下,若是店中有空位放产品了再通知生产者继续生产;若是店中没有产品了,店员会告诉消费者等一下,若是店中有产品了再通知消费者来取走产品。
问题分析:
1.是不是多线程问题?是,生产者线程,消费者线程
2.是否有共享数据?是,店员(或产品)
3.如何解决现成的安全问题?同步机制,有三种方法
4.是否设计线程的通讯?是
问题解决:
/** * @author 🏹☂࿈秋鹜࿈🏹️ * @create 2020/3/7 11:04 */ class Clerk{ private int productCount = 0; //生产产品 public synchronized void produceProduct() { if (productCount<20){ productCount++; System.out.println(Thread.currentThread().getName() + ":正在生产第" + productCount + "个产品"); notify(); }else { try { wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } //消费产品 public synchronized void consumeProduct(){ if (productCount>0){ System.out.println(Thread.currentThread().getName() + ":正在消费第" + productCount + "个产品"); productCount--; notify(); }else { try { wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } } class Producer implements Runnable{//生产者 private Clerk clerk; public Producer(Clerk clerk){ this.clerk = clerk; } @Override public void run() { System.out.println(Thread.currentThread().getName() + ":开始生产产品...."); while (true){ try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } clerk.produceProduct(); } } } class Consumer implements Runnable{//消费者 private Clerk clerk; public Consumer(Clerk clerk){ this.clerk = clerk; } @Override public void run() { System.out.println(Thread.currentThread().getName() + ":开始消费产品...."); while (true){ try { Thread.sleep(20); } catch (InterruptedException e) { e.printStackTrace(); } clerk.consumeProduct(); } } } public class Product { public static void main(String[] args) { Clerk clerk = new Clerk(); Producer producer = new Producer(clerk); Thread p1 = new Thread(producer); p1.setName("生产者1"); Consumer consumer = new Consumer(clerk); Thread c1 = new Thread(consumer); c1.setName("消费者1"); Thread c2 = new Thread(consumer); c2.setName("消费者2"); p1.start(); c1.start(); c2.start(); } }