1.概念
1.概念:多线程是指程序中包含多个执行单元,即在一个程序中可以同时运行多个不同的线程执行不同的任务,也就是说允许单个程序创建多个并行执行的线程来完成各自的任务。
2.何时需要多线程:
(1)程序需要同时执行两个或多个任务;
(2)程序需要实现一些需要等待的任务时,如:用户输入、文件读写操作、网络操作、搜索等;
(3)需要一些后台运行的程序时。
3.多线程的好处:
(1)提高程序响应;
(2)提高CPU利用率;
(3)改善程序结构,将复杂任务分为多个线程,独立运行。
4.多线程的不利之处:
(1)线程也是程序,所以线程需要占用内存,线程越多占用的内存越多;
(2)多线程需要协调和管理,所以需要CPU时间跟踪线程;
(3)线程之间对共享资源的访问会相互影响,必须解决竟用共享资源的问题。
2.线程状态
1.新建:当一个Thread类或其子类的对象被声明并创建时,新生的线程对象处于新建状态;
2.就绪:处于新建状态的线程被start()后,将进入线程队列等待CPU时间片,此时它已经具备了运行条件,只是没分配到CPU资源;
3.运行:当就绪的线程被调度并获得CPU资源时,便进入运行状态,run()方法定义了线程的操作和功能;
4.阻塞:在某种特殊情况下,被人为挂起或执行输入输出操作时,让出CPU并临时终止自己的执行,进入阻塞状态;
5.死亡:线程完成了它的全部工作或线程被提前强制性地中止或出现异常导致结束。
3.线程的分类
java中的线程分为两类:用户线程和守护线程;
守护线程的作用是为其他线程的运行提供便利服务,守护线程最典型的应用就是GC(垃圾回收器),它是一个很称职的守护者。
eg:龟兔赛跑
/**
* 2.使用两个线程模拟龟兔赛跑(使用继承Thread方式实现).
* 假定乌龟和兔子谁先跑够1000步谁就获胜.
*
* 要求最终乌龟获胜.
*/
public class Running extends Thread{
@Override
public void run() {
for(int i=0;i<1000;i++){
if(i%100 == 0){
System.out.println(Thread.currentThread().getName()+"跑了第"+i+"步");
if(Thread.currentThread().getName().equals("兔子")){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}else if(Thread.currentThread().getName().equals("乌龟")){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
if (i == 999) {
System.out.println(Thread.currentThread().getName()+"赢了");
}
}
}
public static void main(String[] args) {
Running running = new Running();
running.setName("乌龟");
running.start();
Running running1 = new Running();
running1.setName("兔子");
running1.setDaemon(true);
running1.start();
}
}
4.线程同步
1.并发与并行
(1)并发:多个CPU同时执行多个任务。
(2)并行:一个CPU(采用时间片)同时执行多个任务。
2.多线程同步:
多个线程同时读写同一份共享资源时,可能会引起冲突。所以引入线程“同步”机制,即各线程间要有先来后到。
3.同步就是排队+锁:
(1)几个线程之间要排队,一个个对共享资源进行操作,而不是同时进行操作;
(2)为了保证数据在方法中被访问的正确性,在访问时加入了锁机制。
eg:模拟买票
public class TicketDemo extends Thread{
static int num = 10;
static Object obj = new Object();
@Override
/*public void run() {
while (true){
if(num == 0){
break;
}
printTicket();
}
}
//给方法加锁,钥匙是this,有多个线程对象,就有多把钥匙,因为有多个对象,不能使用
public synchronized void printTicket(){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
if(num>0){
System.out.println(Thread.currentThread().getName()+"出票:"+num+" "+"还剩:"+(num-1)+"张票");
num--;
}
}*/
public void run() {
while (true){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
//给最核心的,操作共享资源的一段代码加锁,obj就是唯一的一把钥匙
//哪个线程得到了钥匙,就进入同步代码块中,一次只能进入一个线程
synchronized (obj){
if(num>0){
System.out.println(Thread.currentThread().getName()+"出票:"+num+" "+"还剩:"+(num-1)+"张票");
num--;
}else {
break;
}
}
}
}
}
public class Test {
public static void main(String[] args) {
TicketDemo t1 = new TicketDemo();
t1.setName("窗口1");
t1.start();
TicketDemo t2 = new TicketDemo();
t2.setName("窗口2");
t2.start();
}
}
5.线程死锁
1.不同的线程分别占用对方需要的同步资源不放弃,都在等待对方放弃自己需要的同步资源,就形成了线程的死锁,出现死锁后,不会出现异常,不会出现提示,只是所有的线程都处于阻塞状态,无法继续。
public class DelLock extends Thread{
static Object objA = new Object();
static Object objB = new Object();
boolean flag;
public DelLock(boolean flag){
this.flag = flag;
}
@Override
public void run(){
if(flag){
synchronized (objA){
System.out.println("if objA");
synchronized (objB){
System.out.println("if objB");
}
}
}else {
synchronized (objB){
System.out.println("else objB");
synchronized (objA){
System.out.println("else objA");
}
}
}
}
}
public class Test {
public static void main(String[] args) {
DelLock delLock = new DelLock(true);
delLock.start();
DelLock delLock1 = new DelLock(false);
delLock1.start();
}
}
2.避免死锁:
让程序每次只能获得一个锁,当然,在多线程环境下,这种情况通常并不现实,设计时考虑清楚锁的顺序,尽量减少潜在的加锁交互数量。
6.Lock锁
1.synchronized和Lock的区别:
(1)Lock时显示锁(手动开启和关闭),synchronized是隐式锁,出了作用域自动释放;
(2)Lock只有代码块锁,synchronized有代码块锁和方法锁,使用Lock锁,JVM将花费较少的时间来调度线程,性能更好,并且具有更好的扩展性(提供更多的子类);
(3)线程同步优先使用顺序:Lock>同步代码块(已经进入了方法体,分配了相应的资源)>同步方法。
public class LockThread implements Runnable{
int num = 10;
Lock lock = new ReentrantLock();
@Override
public void run() {
while (true){
try{
lock.lock();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if(num>0){
System.out.println(Thread.currentThread().getName()+"出票:"+num+" "+"还剩:"+(num-1));
num--;
}else {
break;
}
}finally {
lock.unlock();
}
}
}
}
public class Test {
public static void main(String[] args) {
LockThread lockThread = new LockThread();
Thread thread = new Thread(lockThread,"窗口1");
Thread thread1 = new Thread(lockThread,"窗口2");
thread.start();
thread1.start();
}
}