多线程笔记

来源:互联网 发布:js中数组删除指定元素 编辑:程序博客网 时间:2024/06/06 16:51
一. 多线程的引入
1.什么是线程?
进程:
线程:
2.多线程的应用:迅雷下载电影,QQ多人视频聊天
3.多线程的原理:CPU在各个进程之间高速切换,如果打开网页多,电脑卡顿
表面上看是多线程
4.并行和并发的区别?
并行:同时执行--多核CPU
并发:两个任务请求一个CPU,而CPU只能处理一个任务,
就安排两个任务轮流执行,由于切换的时间间隔短,看上去像似两个任务同时执行,这个叫并发
5.JVM启动时多线程的吗?
Java命令会启动jvm,启动jvm等于启动了一个进程,
该进程会自动启动一个"主线程",然后该主线程调用某个类的main方法

Java虚拟机在在启动的时候,会启动主线程和垃圾回收线程,所以是多线程的.
--------------------------------------------------
代码示例如下:
public class ThreadTest {
/**
* 证明jvm是多线程的

* 主线程和垃圾回收线程相互争夺资源
* @param args
*/
public static void main(String[] args) {
for (int i = 0; i < 1000000; i++) {
new Cat();
}
for (int i = 0; i < 1000000; i++) {
System.out.println("我是主线程的执行代码");
}
}
}

class Cat{
@Override
public void finalize() throws Throwable {
System.out.println("我被回收了");
}
}
-----------------------------------------------------

二. 多线程的实现方式:
1.方式一,继承Thread,
class MyThread extends Thread{//1. 子类继承父类
@Override
public void run(){//2. 重写父类的run方法
for(int i=0;i<50;i++){//3. 将要执行的代码
System.out.println("aaaaaa");
}
}
}

public static void main(String[] args){
MyThread mt = new MyThread();//创建对象
mt.start(); //调用start()方法,启动线程

//证明和主线程竞争 
for (int i = 0; i < 100; i++) {
System.out.println("bb");
}
}
2. 方式二:实现Runnable接口
class MyRunnable implements Runnable{//1. 实现Runnable接口
@Override
public void run(){//2. 重写父类的run方法
for(int i=0;i<50;i++){//3. 将要执行的代码
System.out.println("aaaaaa");
}
}
}
public static void main(String[] args){
MyRunnable mr = new MyRunnable();//创建对象
Thread t = new Thread(mr);//创建线程对象
t.start(); //调用start()方法,启动线程
//证明和主线程竞争
for (int i = 0; i < 100; i++) {
System.out.println("bb");
}
}
3. 实现Runnable接口的原理
凭什么t.start();这个代码会调用类MyRunnable里面的run方法??查看源码
4. 两种方式的区别?
A:源码的区别:
B:类和接口的区别:

实际开发中,推荐实现Runnable接口
5. 匿名内部类实现线程的两种方式
A:继承Thread类
new Thread(){
public void run() {
for (int i = 0; i < 50; i++) {
System.out.println("aaaaa");
}
};
}.start();
for (int i = 0; i < 50; i++) {
System.out.println("bb");
}
B:实现Runnable接口
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
System.out.println("aaaaa");
}
}
}).start();


三. 线程的一些操作
1.设置名字和获取名字
A:设置名字:
a:通过构造方法 Thread t1 = new Thread(String name);
b:通过setName()方法t1.setName("线程1");


new Thread("凤姐"){
public void run() {
this.setName("张三");
System.out.println(this.getName()+"..."+"aaaa");//张三...aaaa
};
}.start();
B: 获取名字:
通过getName()方法


2.获取当前线程对象:Thread tt = Thread.currentThread();
3.休眠线程:Thread.sleep(100);
线程休眠时,CPU会让出执行权,交给其他的线程执行
当线程醒来时,才可能抢夺到CPU的执行权。
4.守护线程:setDaemon(), 
设置一个线程为守护线程, 该线程不会单独执行, 当其他非守护线程都执行结束后, 自动退出
举例:中国的象棋
非守护线程---老帅   守护线程--车马象仕
5.加入线程
6.礼让线程
7.设置线程的优先级:t1.setPriority(Thread.MAX_PRIORITY);


四:同步问题
什么情况下需要同步?
当多线程并发, 有多段代码同时执行时, 
我们希望某一段代码执行的过程中CPU不要切换到其他线程工作. 这时就需要同步.

如果两段代码是同步的, 那么同一时间只能执行一段, 
在一段代码没执行结束之前, 不会执行另外一段代码.


1.同步代码块:
A:使用synchronized关键字加上一个锁对象来定义一段代码, 这就叫同步代码块

B:多个同步代码块如果使用相同的锁对象(锁对象可以是任意对象), 那么他们就是同步的


/*------------------------------*/
class Printer {
Object obj = new Object();
public void print1() {
synchronized(obj){
//锁对象可以是任意对象,但是被锁的代码需要保证是同一把锁,不能用匿名对象
System.out.print("我");
System.out.print("爱");
System.out.print("苍");
System.out.print("井");
System.out.print("空");
System.out.print("\r\n");
}
}


public void print2() {
synchronized(obj){
System.out.print("s");
System.out.print("y");
System.out.print("n");
System.out.print("c");
System.out.print("h");
System.out.print("r");
System.out.print("o");
System.out.print("n");
System.out.print("i");
System.out.print("z");
System.out.print("e");
System.out.print("d");
System.out.print("\r\n");
}
}
}


Printer p = new Printer();
new Thread(()->{ while(true){p.print1();} }).start();//Lambda表达式
new Thread(()->{ while(true){p.print2();} }).start();
/*------------------------------*/

2.同步方法
使用 synchronized 关键字修饰一个方法, 该方法中所有的代码都是同步的


问题:锁对象是什么?
如果是非静态方法,锁对象是this
如何证明?
证明代码如下:
public synchronized void print1() {
System.out.print("我");
System.out.print("爱");
System.out.print("苍");
System.out.print("井");
System.out.print("空");
System.out.print("\r\n");
}


public void print2() {
synchronized(this){
System.out.print("s");
System.out.print("y");
System.out.print("n");
System.out.print("c");
System.out.print("\r\n");
}
}


如果是静态方法,锁对象是该类的字节码对象
如何证明?
证明代码如下:
class Printer2 {
public static synchronized void print1() {
System.out.print("我");
System.out.print("爱");
System.out.print("苍");
System.out.print("井");
System.out.print("空");
System.out.print("\r\n");
}


public static void print2() {
synchronized(Printer2.class){
System.out.print("s");
System.out.print("y");
System.out.print("n");
System.out.print("c");
System.out.print("\r\n");
}
}
}


3.线程安全问题--继承 Thread 类来解决
多线程并发操作同一数据时, 就有可能出现线程安全问题
使用同步技术可以解决这种问题, 把操作数据的代码进行同步, 不要多个线程一起操作
需求:铁路售票,一共100张,通过四个窗口卖完.


public static void main(String[] args){
new Ticket("窗口1").start();
new Ticket("窗口2").start();
new Ticket("窗口3").start();
new Ticket("窗口4").start();
}


class Ticket extends Thread {
public Ticket(){}
public Ticket(String name){
super(name);
}
private static int num = 100;
private static Object obj = new Object();//锁对象为什么要被static修饰?
@Override
public void run() {
while (true) {
synchronized (obj) {
//注意锁对象是什么,注意synchronized的位置
//这里也可以使用该类的字节码锁对象
if (num == 0) {
break;
}
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(getName() + "  这是第" + (num--) + "张票");
}
}

}
}
4.线程安全问题--实现 Runnable 接口 来解决
public static void main(String[] args) {
Runnable r = new Ticket();
new Thread(r).start();
new Thread(r).start();
new Thread(r).start();
new Thread(r).start();
}


class Ticket implements Runnable{
private int num = 100;
@Override
public void run() {
while(true){
synchronized (this) {
if(num<=0){
break;
}
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+" "+(num--));
}
}
}
}







原创粉丝点击