什么叫做单线程
栗子:
如果这个页面设置不完成的话,就无法进入到记事本当中。这就是单线程,必须把一件事做完才能做另外一件事。
什么叫多线程
栗子:
在扫雷中,一旦开始游戏,计时器就开始计时了,而不会等你走完下一步,游戏和时间是分别进行的,这就是多线程。
线程:是进程中的单个顺序控制流,是一条执行路径。
- 单线程: -一个进程如果只有一条执行路径,则称为单线程程序。
- 多线程: -个进程如果有多条执行路径,则称为多线程程序
package Thread;
public class MyThreadDemo {
public static void main(String[] args) {
MyThread t1=new MyThread();
MyThread t2=new MyThread();
// t1.run();
// t2.run();
//void start () 导致此线程开始执行; Java虛拟机调用此线程的run方法
t1.start();
t2.start();
}
}
package Thread;
public class MyThread extends Thread{
@Override
public void run() {
for (int i=0;i<1000;i++){
System.out.println(i);
}
}
}
执行结果:从253开始第二个线程启动了。
package Thread;
public class MyThreadDemo {
public static void main(String[] args) {
MyThread t1=new MyThread("飞机");
MyThread t2=new MyThread("高铁");
// t1.run();
// t2.run();
//void start () 导致此线程开始执行; Java虛拟机调用此线程的run方法
t1.start();
t2.start();
System.out.println(Thread.currentThread().getName());
}
}
public class MyThread extends Thread{
public MyThread() {
}
public MyThread(String name) {
super(name);
}
@Override
public void run() {
for (int i=0;i<1000;i++){
System.out.println(getName()+":"+i);
}
}
}
测试使用sleep:
package Thread;
public class ThreadSleepDemo {
public static void main(String[] args) {
ThreadSleep ts1=new ThreadSleep("曹操");
ThreadSleep ts2=new ThreadSleep("刘备");
ThreadSleep ts3=new ThreadSleep("孙权");
ts1.start();
ts2.start();
ts3.start();
}
}
package Thread;
public class ThreadSleep extends Thread{
public ThreadSleep() {
}
public ThreadSleep(String name) {
super(name);
}
@Override
public void run() {
for (int i=0;i<1000;i++){
System.out.println(getName()+":"+i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
执行结果:
每个线程,自己执行一次就sleep一秒,但是执行一次是特别快的,所以相当于每一秒每个只执行一次了,但是执行的顺序是按照抢占调度模式来的。
Join方法:等待这个线程死亡才会继续向下执行别的线程。
public class ThreadJoinDemo {
public static void main(String[] args) {
ThreadJoin ts1=new ThreadJoin("康熙");
ThreadJoin ts2=new ThreadJoin("四阿哥");
ThreadJoin ts3=new ThreadJoin("八阿哥");
ts1.start();
try {
ts1.join();//等待康熙执行完毕之后,之后两个线程才执行
} catch (InterruptedException e) {
e.printStackTrace();
}
ts2.start();
ts3.start();
}
}
Daemon方法:设置某一个线程为守护线程,也就是非主要线程,当主线程内容执行完成之后,如果只剩下守护线程,则Java虚拟机退出,不在继续执行。
package Thread;
public class ThreadDaemonDemo {
public static void main(String[] args) {
MyThread ts1=new MyThread("关羽");
MyThread ts2=new MyThread("张飞");
Thread.currentThread().setName("刘备");
ts1.setDaemon(true);
ts2.setDaemon(true);
ts1.start();
ts2.start();
for (int i=0;i<10;i++){
System.out.println( Thread.currentThread().getName()+":"+i);
}
}
}
package Thread;
public class MyRunnable implements Runnable {
@Override
public void run() {
for (int i =0;i<1000;i++){
System.out.println(Thread.currentThread().getName()+i);
}
}
}
public class MyRunnableDemo {
public static void main(String[] args) {
MyRunnable mr=new MyRunnable();
Thread t1=new Thread(mr);
Thread t2=new Thread(mr);
t1.start();
t2.start();
}
}
相比继承Thread类,实现Runnable接口的好处
-
避免了Java单继承的局限性(因为使用Runnable实现类,可以再去继承别的类了)
-
适合多个相同程序的代码去处理同一个资源的情况,把线程和程序的代码、数据有效分离,较好的体现了面向对象的设计思想。(因为这样只使用了Thread其中一个接口的功能,比起直接继承,耦合度更低了)
多线程的实质依然是一个cpu进行分时处理多个进程,只是从宏观上来看好像是同时进行的,实际微观上是分步进行的。
package Thread;
public class SellTicket implements Runnable{
private int tickets=100000000;
private Object lock =new Object();//使用同一把锁来锁起来
@Override
public void run() {
while (true) {
//同步锁
synchronized (lock) {
if (tickets > 0) {
//sleep 100ms
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
//卖票
System.out.println(Thread.currentThread().getName() + "正在售出第" + tickets + "张票");
tickets--;
}
}
}
}
}
package Thread;
public class SellTicketDemo {
public static void main(String[] args) {
SellTicket st=new SellTicket();
Thread t1=new Thread(st,"窗口1");
Thread t2=new Thread(st,"窗口2");
Thread t3=new Thread(st,"窗口3");
t1.start();
t2.start();
t3.start();
}
}
执行结果:
package Thread;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class SellTicket implements Runnable {
private static int tickets = 1000;
private Object lock = new Object();//使用同一把锁来锁起来
private int x = 0;
private Lock lockk = new ReentrantLock();
@Override
public void run() {
while (true) {
try {
lockk.lock();
if (x % 2 == 0) {
//同步锁
if (tickets > 0) {
//sleep 100ms
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
//卖票
System.out.println(Thread.currentThread().getName() + "正在售出第" + tickets + "张票");
tickets--;
}
}
} finally {
lockk.unlock();
}
x++;
}
}
}
为了防止出现异常而导致unlock没有执行,也就是只上锁而没有释放锁的情况,使用try,finally。
生产者消费者模式
需要注意:使用wait和notify这种等待唤醒操作,必须使用synchronized修饰
如:
public synchronized void put(int milk){//使用wait和notify这种等待唤醒操作,必须使用synchronized修饰
//如果有牛奶,等待消费了之后才能继续往box里面放牛奶
if (state){
try {
wait();
}catch (InterruptedException e){
e.printStackTrace();
}
}
//如果没有牛奶,就生产牛奶
this.milk=milk;
System.out.println("送奶工将第"+this.milk+"瓶奶放入奶箱中");
//生产完毕后,修改奶箱状态
state=true;
notifyAll();
}
Box:
package Thread;
public class Box {
private int milk;
private boolean state=false;
public synchronized void put(int milk){//使用wait和notify这种等待唤醒操作,必须使用synchronized修饰
//如果有牛奶,等待消费了之后才能继续往box里面放牛奶
if (state){
try {
wait();
}catch (InterruptedException e){
e.printStackTrace();
}
}
//如果没有牛奶,就生产牛奶
this.milk=milk;
System.out.println("送奶工将第"+this.milk+"瓶奶放入奶箱中");
//生产完毕后,修改奶箱状态
state=true;
notifyAll();
}
public synchronized void get(){
//如果没有牛奶,等待生产好了,box里面有牛奶了才能取牛奶
if(!state){
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("用户拿到第"+this.milk+"瓶奶");
state=false;
//唤醒其他等待进程
notifyAll();
}
}
Customer:
package Thread;
public class Customer implements Runnable{
private Box b;
public Customer(Box b) {
this.b=b;
}
@Override
public void run() {
while (true){
b.get();
}
}
}
Producer:
package Thread;
public class Producer implements Runnable{
private Box b;
public Producer(Box b) {
this.b=b;
}
@Override
public void run() {
for (int i=1;i<=5;i++){
b.put(i);
}
}
}
主程序:
package Thread;
public class BoxDemo {
public static void main(String[] args) {
Box b=new Box();
Producer p=new Producer(b);
Customer c=new Customer(b);
//创建生产者线程与消费者线程
Thread tp=new Thread(p);
Thread tc=new Thread(c);
tp.start();
tc.start();
}
}
执行结果: