线程介绍
-
Java 对多线程支持。 一条线程指的是进程中一个任务,一个进程中可以并发多个线程,每条线程并行执行不同的任务。
-
进程:一个进程包括由操作系统分配的内存空间,包含一个或多个线程。一个线程不能独立的存在,它必须是进程里运行。一个进程一直运行,直到所有的非守护线程都结束运行后才能结束。
-
多线程能充分利用cpu,提高执行效率。
如何创建子线程
继承Thread
1.重写Thread类的run()方法.
2.创建子线程对象,调用start()启动子线程
package com.njlife123.qzmall.base;
public class Test6 {
public static void main(String[] args) {
Task task = new Task();
task.start();
}
}
class Task extends Thread{
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}
打印结果:
Thread-0 //当前线程名字
Process finished with exit code 0
实现Runnable
实现其中的run()方法
实例化Runnable接口的实例,传入Thread()的构造函数,
创建一个子线程对象,
调用start()启动子线程
package com.njlife123.qzmall.base;
public class Test6 {
public static void main(String[] args) {
Task task = new Task();
Thread thread = new Thread(task);
thread.start();
}
}
class Task implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}
线程创建也可以参考:http://www.njlife123.com/article/26 这篇文章
线程生命周期
线程常见操作
启动
线程对象.start(); //启动线程,让线程进入可运行状态
sleep
Thread.sleep()使当前线程的执行暂停一段指定的时间(单位是毫秒),这可以有效的使应用程序的其他线程或者运行在计算机上的其他进程可以使用处理器时间。
该方法不会放弃除CPU之外的其它资源。
join
Join方法让一个线程等待另一个线程的完成。
比如:在main线程调用join main方法会等thread执行完在执行。
package com.njlife123.qzmall.base;
public class Test6 {
public static void main(String[] args) throws InterruptedException {
Task task = new Task();
Thread thread = new Thread(task);
thread.start();
thread.join();//在main线程调用join main方法会等thread执行完在执行
System.out.println("main");
}
}
class Task implements Runnable{
@Override
public void run() {
try {
Thread.sleep(2000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName());
}
}
线程的终止
通过标志位终止
package com.njlife123.qzmall.base;
public class Test6 {
//volatile 内存可见
volatile static boolean flag = false;
public static void main(String[] args) throws InterruptedException {
Task task = new Task();
Thread thread = new Thread(task);
thread.start();
Thread.sleep(3000L);
flag = true;
}
static class Task implements Runnable{
@Override
public void run() {
while (!flag)
System.out.println(Thread.currentThread().getName());
}
}
}
通过flag标志位,三秒后主线程修改标注位导致线程终止。
线程的优先级
Java 中的线程优先级是在 Thread 类中定义的常量
NORM_PRIORITY : 值为 5
MAX_PRIORITY : 值为 10
MIN_PRIORITY : 值为 1
缺省优先级为 NORM_PRIORITY
有关优先级的方法有两个:
final void setPriority(int newp) : 修改线程的当前优先级
final int getPriority() : 返回线程的优先级
package com.njlife123.qzmall.base;
public class Test6 {
public static void main(String[] args) throws InterruptedException {
Task task = new Task();
Thread thread1 = new Thread(task);
Thread thread2 = new Thread(task);
thread1.setPriority(Thread.MAX_PRIORITY);
thread2.setPriority(Thread.MIN_PRIORITY);
thread1.start();
thread2.start();
}
static class Task implements Runnable{
@Override
public void run() {
while (true)
System.out.println(Thread.currentThread().getName());
}
}
}
通过运行代码可见thread1 机会比较多。
线程的种类
普通线程:
守护线程
当一个进程里的所有普通线程都结束,守护线程就over。
我们通过setDaemon(boolean on)来设置当前线程是否为守护线程。
比如:
package com.njlife123.qzmall.base;
public class Test6 {
public static void main(String[] args) throws InterruptedException {
Task1 task1 = new Task1();
Task2 task2 = new Task2();
task2.setDaemon(true);//thread2是守护线程
task1.start();
task2.start();
}
static class Task1 extends Thread{
@Override
public void run() {
for (int i=0;i<1000;i++)
System.out.println(Thread.currentThread().getName()+"普通任务线程");
}
}
static class Task2 extends Thread{
@Override
public void run() {
while (true)
System.out.println(Thread.currentThread().getName()+"守护线程");
}
}
}
task1停止了,当前进程就没有普通线程了,守护线程就结束了。我们的jvm垃圾收集线程就是守护线程。
线程同步
每个线程有自己单独的寄存器和栈,所有线程共享堆。
避免同一时间两个线程去访问同一资源,导致数据不一致。
同步基于“监督锁”这一概念。“监督锁”是用作互斥锁的对象。在给定时刻,只有一个线程可以拥有监督锁。
Java中所有的对象都拥有自己的监督锁。
两种方式实现同步:
- 同步块
synchronized(object) {
//要同步的语句
}
public void f1(){
synchronized (obj){
System.out.println("获取锁");
}
}
- 同步方法
synchronized void methodA() { }
同步对象为this
public synchronized void f1(){
System.out.println("获取锁");
}
线程安全类
public class Test6 {
public synchronized void f1(){
}
public synchronized void f2(){
}
public synchronized void f3(){
}
}
一个类所有的方法都是synchronized 修饰,称之为线程安全类。
假如有多个线程操作此类的同一个实例
假如线程1进入了f1()方法,在线程1的f1()方法执行完毕前,其它线程是不能进入其它任何有synchronized修饰的方法通过这种方式,保证数据的一致性。
常见线程安全类:Vector、HashTable、StringBuffer
都是线程安全类。
死锁
相互都有获取一把锁,相互都需要对方已获得的锁才可以执行就产生死锁。
代码:
package com.njlife123.qzmall.base;
public class Test6 {
//o1锁
private Object o1 = new Object();
//o2锁
private Object o2 = new Object();
/**
* 线程现货区o1锁和o2锁才能运行
*/
public void f1() {
synchronized (o1){
try {
Thread.sleep(2000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"已经获得o1锁等待获取o2锁");
synchronized (o2){
System.out.println("f1");
}
}
}
/**
* 线程要获取o2锁和o1锁才能运行
*/
public void f2(){
synchronized (o2){
try {
Thread.sleep(2000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"已经获得o2锁等待获取o1锁");
synchronized (o1){
System.out.println("f2");
}
}
}
class Task1 extends Thread{
@Override
public void run() {
f1();
}
}
class Task2 extends Thread{
@Override
public void run() {
f2();
}
}
public void mt(){
Task1 task1 = new Task1();
Task2 task2 = new Task2();
task1.start();
task2.start();
}
public static void main(String[] args) {
Test6 test6 = new Test6();
test6.mt();
}
}
打印结果:
Thread-0已经获得o1锁等待获取o2锁
Thread-1已经获得o2锁等待获取o1锁
程序僵住了。产生死锁了。
线程交互
Java提供了一个精心设计的线程间通信机制,使用wait()、notify()和notifyAll()方法 。
这些方法是作为 Object 类中的 final 方法实现的。
wait()方法告知被调用的线程退出监视器并进入等待状态,直到其他线程进入相同的监视器并调用 notify( ) 方法。
notify( ) 方法通知同一对象上第一个调用 wait( )线程。
notifyAll() 方法通知调用 wait() 的所有线程,具有最高优先级的线程将先运行。
注意: notify(),wait()必须用在同步的方法里。
ThreadLocal类
该类的set进去的变量其他线程不能访问,只能在本线程访问。
它有两个方法set和get方法。
package com.njlife123.qzmall.base;
public class Test7 {
static ThreadLocal local = new ThreadLocal();
public static void main(String[] args) {
new Task1().start();
new Task2().start();
}
static class Task1 extends Thread{
@Override
public void run() {
local.set("aaaa");
}
}
static class Task2 extends Thread{
@Override
public void run() {
System.out.println(local.get());
}
}
}
Task1设置进去的变量aaaa,Task1是没法取到的。
作者简介:一个有故事的程序员,我的微信号qiushuzhao222,欢迎大家找我聊天,记录你我的故事。