概念
实现方式
线程方法
线程同步
线程安全
线程死锁
线程通讯
线程互斥锁
概念
多线程:如果把QQ比作进程,那么既聊天,又玩农场,就是多线程
并发:甲任务与乙任务切换进行
并行:甲任务与乙任务同时进行
举例:jvm是多线程,Jvm启动了垃圾回收线程和主线程
实现方式:三种
第一种---Thread类
public class HelloWorld {
public static void main(String[] args) {
//3、启动start
new thread().start();
for (int i=0;i<100000;i++){
System.out.println("主线程");
}
}
}
//1、继承Thread
class thread extends Thread{
//2、实现run方法
public void run(){
for (int i=0;i<100000;i++){
System.out.println("副线程");
}
}
}
第二种---Runnable接口
public class HelloWorld {
public static void main(String[] args) {
//3、调用Thread类
Thread th = new Thread(new thread());
//4、启动start方法
th.start();
for (int i=0;i<100000;i++){
System.out.println("主线程");
}
}
}
//1、实现Runnable
class thread implements Runnable{
//2、实现run方法
public void run(){
for (int i=0;i<100000;i++){
System.out.println("副线程");
}
}
}
第三种---匿名方式
public class HelloWorld {
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
for (int i=0;i<100000;i++){
System.out.println("第一线程");
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
for (int i=0;i<100000;i++){
System.out.println("第二线程");
}
}
}).start();
}
}
线程方法
1、优先级 setPriority()
2、睡眠时间 sleep()
3、线程插队 join()
4、礼让线程 yield()
5、后台运行 setDaemon(true)
public class HelloWorld {
public static void main(String[] args) {
Thread th=new Thread(new Runnable() {
@Override
public void run() {
for (int i=0;i<100000;i++){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("第一线程");
}
}
});
Thread th2=new Thread(new Runnable() {
@Override
public void run() {
for (int i=0;i<100000;i++){
if (i == 9999){
try {
th.join();//线程插队
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if (i == 54654){
Thread.yield();//让出CPU
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("第二线程");
}
}
});
th.setPriority(8);//从1-10 逐次增加
th.setDaemon(true);//设置守护线程(后台运行,占资源少)
th.start();
th2.start();
}
}
线程同步
package exercise;
/*
当多线程并发, 有多段代码同时执行时, 我们希望某一段代码执行的过程中
CPU不要切换到其他线程工作,这时就需要同步。如果两段代码是同步的,
那么同一时间只能执行一段, 在一段代码没执行结束之前, 不会执行另外一段
代码。所谓同步代码块就是使用synchronized关键字加上一个锁对象来定义
一段代码,这就叫同步代码块,多个同步代码块如果使用相同的锁对象,那
么他们就是同步的。
* */
class exercise{
//开始跑类方法
public static void main(String[] args) {
Print print=new Print();
new Thread(){
public void run(){
while(true){
print.print();
}
}
}.start();
new Thread(){
public void run(){
while(true){
print.print1();
}
}
}.start();
}
}
//创建对象,创建方法
class Print {
Demo demo=new Demo();
public void print(){
synchronized (demo) {
System.out.println("黑马");
System.out.println("程序");
}
}
public void print1(){
synchronized(demo){
System.out.println("传智");
System.out.println("博客");
}
}
}
//要跑的类
class Demo{}
线程安全
多线程安全问题:两个线程在同时刻,只能其中一个参与运算,另外一个必须等待。
通俗理解:两个同学在餐厅同时抢第一个位置,结果自己想吧!
解决方法:给线程上锁,在这个线程上锁期间,不能有其他线程介入运行,这样就解决问题了!
public class HelloWorld {
public static void main(String[] args) {
new Ticket().start();
new Ticket().start();
new Ticket().start();
new Ticket().start();
}
}
class Ticket extends Thread{
private static int ticket = 100;
public void run(){
while (true){
//上锁
synchronized (Ticket.class){
if(ticket<=0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
break;
}
System.out.println(getName()+"第"+ ticket-- +"张票售出");
}
}
}
}
线程死锁
/*一个线程持有锁,没释放这个锁之前,其他线程获取不到此锁,
如果其他线程非要此锁,就要死等,这种情况,就是死锁。
*/
线程通讯
等待:wait
单个唤醒:notify
全部唤醒:notifyAll
public class HelloWorld {
public static void main(String[] args) {
Ticket t = new Ticket();
new Thread(){
public void run(){
while (true){
try {
t.print1();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();
new Thread(){
public void run(){
while (true){
try {
t.print2();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();
new Thread(){
public void run(){
while (true){
try {
t.print3();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();
}
}
class Ticket {
private int ticket = 1;
public void print1() throws InterruptedException {
synchronized (this){
if (ticket != 1){
this.wait();//睡眠
}
System.out.println("线程一");
ticket=2;
this.notify();//唤醒
}
}
public void print2() throws InterruptedException {
synchronized (this){
if (ticket != 2){
this.wait();//睡眠
}
System.out.println("线程二");
ticket=3;
this.notify();//唤醒
}
}
public void print3() throws InterruptedException {
synchronized (this){
if (ticket != 3){
this.wait();//睡眠
}
System.out.println("线程三");
ticket=1;
this.notify();//唤醒
}
}
}
线程互斥锁
ReentrantLock
Condition
/*
* 互斥锁是JDK1.5版本的新特性,包含了多线程通讯、多线程锁,比起之前的更具有灵活性。
*
* */
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
public class HelloWorld {
public static void main(String[] args) {
Ticket t = new Ticket();
new Thread(){
public void run(){
while (true){
try {
t.print1();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();
new Thread(){
public void run(){
while (true){
try {
t.print2();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();
new Thread(){
public void run(){
while (true){
try {
t.print3();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();
}
}
class Ticket {
private ReentrantLock r = new ReentrantLock();//互斥锁
private Condition c1 = r.newCondition();//通讯1
private Condition c2 = r.newCondition();//通讯2
private Condition c3 = r.newCondition();//通讯3
private int ticket = 1;
public void print1() throws InterruptedException {
r.lock();//启动锁
if (ticket != 1){
c1.await();//等待
}
System.out.println("线程一");
ticket=2;
c1.signal();//唤醒
r.unlock();//关闭锁
}
public void print2() throws InterruptedException {
r.lock();//启动锁
if (ticket != 2){
c2.await();//睡眠
}
System.out.println("线程二");
ticket=3;
c2.signal();//唤醒
r.unlock();//关闭锁
}
public void print3() throws InterruptedException {
r.lock();//启动锁
if (ticket != 3){
c3.await();//睡眠
}
System.out.println("线程三");
ticket=1;
c3.signal();//唤醒
r.unlock();//关闭锁
}
}
线程组
//线程组:安全第一,A在1线程组内,A就不能修改2线程组数据。
//线程池:效率第一,A线程干完活,不结束他,让他睡眠,需要再唤醒。
package exercise;
public class exercise {
public static void main(String[] args) {
ThreadGroup tg = new ThreadGroup("我是一个新线程");//创建线程组
MyRunnable mr = new MyRunnable();//创建Runnable的子类对象
Thread t1= new Thread(tg,mr,"张三");//将线程t1放在组中
Thread t2= new Thread(tg,mr,"李四");//将线程t2放在组中
System.out.println(t1.getThreadGroup().getName());//获取组名
System.out.println(t2.getThreadGroup().getName());
}
}
class MyRunnable implements Runnable{
public void run() {
for(int i=0;i<100;i++) {
System.out.println(Thread.currentThread().getName());
}
}
}
线程池
//一般我们创建少量线程,并不会出现太大问题,可是实际开发中我们
//可能要用到n多线程,频繁创建和销毁线程会严重销毁系统资源,
//针对这一问题,出现了线程池技术,线程池可以对线程回收再利用
package exercise;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class exercise {
public static void main(String[] args) {
ExecutorService pool = Executors.newFixedThreadPool(2);//第四步,创建线程池对象
pool.submit(new MyRunnable());//第五步,将线程放到池子里
pool.submit(new MyRunnable());
pool.shutdown();//第六步,关闭线程池
}
}
class MyRunnable implements Runnable{//第一步
public void run() {//第二步
for(int i=0;i<100;i++) {//第三步
System.out.println(Thread.currentThread().getName());
}
}
}
线程生命周期
线程定时器(Timer)
package exercise;
import java.io.IOException;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;
class exercise {
@SuppressWarnings("deprecation")
public static void main(String[] args) throws IOException, InterruptedException {
Timer t = new Timer();//第四步,创建计数器
t.schedule(new Time(), new Date(118,8,0,20,28,00));//new time();时间到了显示起床啦
//new Data();设定时间
//年是2018-1900年
//月是9月
//日是1日
//时分秒 21点 23分 00秒
while(true) {//设定时间
Thread.sleep(1000);
System.out.println(new Date());
}
}
}
class Time extends TimerTask{//第一步
public void run() {//第二步
System.out.println("起床啦");//第三步
}
}