昨天知识总结
- TreeMap的注意点
- 增强for循环
- Arrays
- 可变参数
- Collections
- 多线程
- 多线程的基础
- 多线程的原理
- 多线程的实例—垃圾回机制
- 多线程的创建方式
线程
线程创建的两种方法
- 实例:实现两个售票员售票
- 分析:创建4个线程,–4个售票员
- 任务:只需要一个
- 数据:只需要一个
- 创建线程的第一种方法:通过创建Thread类的子类—让run留在了线程的内部,造成任务与线程的绑定,操作不方便
public static void main(String[] args) {
Seller seller1 = new Seller();
Seller seller2 = new Seller();
Seller seller3 = new Seller();
Seller seller4 = new Seller();
seller1.start();
seller2.start();
seller3.start();
seller4.start();
}
class Seller extends Thread{
static int num = 40;
public void run() {
for(int i = 0; i < 10;i++) {
System.out.println(Thread.currentThread().getName()+" i:"+i+" "+--num);
}
}
}
- 第二种方式:让线程与任务分离----将run从线程中独立出来,好处:操作起来更方便,那个线程想工作,我就把任务交给谁。
public static void main(String[] args) {
Ticket ticket = new Ticket();
Thread seller1 = new Thread(ticket);
Thread seller2 = new Thread(ticket);
Thread seller3 = new Thread(ticket);
Thread seller4= new Thread(ticket);
seller1.start();
seller2.start();
seller3.start();
seller4.start();
}
}
class Ticket implements Runnable{
int num = 40;
public void run() {
for(int i = 0; i < 10;i++) {
System.out.println(Thread.currentThread().getName()+" i:"+i+" "+--num);
}
}
}
线程安全和解决方法
- 线程安全问题:
- 分析:4个线程共用一个数据,出现了-1,-2,-3等错误的数据
- 具体分析:1.共用了一个数据
- 2.共有语句有多余,一个线程使用cpu,没有使用完,CPU被抢走,当再次抢到cpu的时候,直接执行后面的语句,造成了错误的发生
- 解决:
- 在代码中使用同步代码块儿(同步锁)
- 解释:在某一段任务中,同一时间只允许一个线程执行任务,其他的线程即使抢到了CPU,也无法进入当前的任务区间,只有当当前的线程将任务执行完后,其他的线程才能有资格进入
- 同步代码块儿的构成:
- synchronized(锁(对象)){
- 同步的代码
- }
- 对做为锁的对象的要求:1.必须是对象 2.必须保证被多个线程共享
- 可以充当锁的:
- 一个普通的对象
- 当前对象的引用----this
- 类的字节码文件
- 同步代码块儿的特点:
- 可以保证线程安全
- 由于每次都要进行判断处理,所以降低了执行效率
- 总结:什么时候使用同步代码块儿?
- 多个线程共享一个数据
- 至少有两个线程
public static void main(String[] args) {
Ticket1 ticket = new Ticket1();
Thread seller1 = new Thread(ticket);
Thread seller2 = new Thread(ticket);
Thread seller3 = new Thread(ticket);
Thread seller4= new Thread(ticket);
seller1.start();
seller2.start();
seller3.start();
seller4.start();
}
}
class Ticket1 implements Runnable{
int num = 20;
boolean flag = false;
Object object = new Object();
public void run() {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
while(!flag) {
synchronized (this) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
if(num > 0) {
System.out.println(Thread.currentThread().getName()+" "+--num);
}else {
flag = true;
}
}
}
}
}
同步函数
- 实例:两个人向同一个账户里面存钱
- 一人存三次,每次存100
- 注意:1.当一个类中同时存在多个synchronized修饰的代码块儿或函数式,要想安全,就必须让他们后面的对象一致,因为只有同一把锁才安全。
- 静态同步函数的锁是其所属类的字节码文件
- 理解synchronized关键字
- 1.synchronized的关键字的作用域有两种:
- a. 是某个对象实例内:synchronized aMethod(){}可以防止多个线程同事访问这个对象的synchronized方法(如果一个对象有多个synchronized方法,只要一个线程访问了其中一个synchronized方法,其他线程不能同时访问这个对象中的任何一个synchronized方法)。这时不同的对象实例的synchronized方法是不想干扰的。也就是说,其他线程照样可以同时访问相同类的另一个对象实例中的synchronized方法。
public static void main(String[] args) {
Ticket2 ticket2 = new Ticket2();
Thread thread1 = new Thread(ticket2);
Thread thread2 = new Thread(ticket2);
thread1.start();
thread2.start();
}
}
class Ticket2 implements Runnable{
Bank bank = new Bank();
@Override
public void run() {
for(int i = 0; i < 3;i++) {
bank.addMany(100);
}
}
}
class Bank{
int sum;
public synchronized void addMany(int money) {
sum+=money;
System.out.println(sum);
}
}
-
- b. 是某个类的范围:synchronized static aStaticMethod{}防止多个线程同时访问这个类中的synchronized static方法。他可以对类的所有对象实例起到作用。
public static void main(String[] args) {
Ticket2 ticket2 = new Ticket2();
Thread thread1 = new Thread(ticket2);
Thread thread2 = new Thread(ticket2);
thread1.start();
thread2.start();
}
}
class Ticket2 implements Runnable{
Bank bank = new Bank();
@Override
public void run() {
for(int i = 0; i < 3;i++) {
bank.addMany(100);
}
}
}
class Bank{
int sum;
public synchronized static void addMany(int money) {
}
}
- 2.除了方法前用synchronized关键字,synchronized关键字还可以用于方法中的某个区块中,表示只对这个区块的资源实行互斥访问。用法是:synchronized(this){//区块/},它的作用域是当前对象;
public static void main(String[] args) {
Ticket2 ticket2 = new Ticket2();
Thread thread1 = new Thread(ticket2);
Thread thread2 = new Thread(ticket2);
thread1.start();
thread2.start();
}
}
class Ticket2 implements Runnable{
Bank bank = new Bank();
@Override
public void run() {
for(int i = 0; i < 3;i++) {
bank.addMany(100);
}
}
}
class Bank{
int sum;
public void addMany(int money) {
synchronized (this) {
sum+=money;
System.out.println(sum);
}
}
}
- 3.synchronized关键字是不能被继承的,也就是说,基类的方法synchronized f(){}
- 在继承类中并不自动式synchronizedf(){},而是变成了f(){},继承类需要你显示的指定它的某个方法为synchronized方法
在懒汉式单例中添加锁
![java第17天----线程的创建,线程安全,单例中的线程安全,线程的非正常停止方法 java第17天----线程的创建,线程安全,单例中的线程安全,线程的非正常停止方法](/default/index/img?u=aHR0cHM6Ly9waWFuc2hlbi5jb20vaW1hZ2VzLzg2MS9kNzllZjRmNjY5MDgwYTgyMTU3ZTY5ZmU3YmNkYmQ5ZC5wbmc=)
class SingleInstance1{
private static SingleInstance1 instance1;
private SingleInstance1() {
}
public static SingleInstance1 getInstance() {
if(instance1 == null) {
synchronized (SingleInstance1.class) {
if(instance1 == null) {
instance1 = new SingleInstance1();
}
}
}
return instance1;
}
}
线程的停止
1.通过一个标识结束线程
public static void main(String[] args) {
Test1 test1 = new Test1();
Thread thread = new Thread(test1);
thread.start();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
int i = 0;
while(true) {
if(++i == 1000000000) {
test1.flag = false;
break;
}
}
}
}
class Test1 implements Runnable{
boolean flag = true;
@Override
public void run() {
while(flag) {
System.out.println(Thread.currentThread().getName()+" 我们很嗨皮");
}
}
}
2.通过stop方法结束线程----有固有的安全问题,已经过时,不建议在使用
3.调用interrupt()方法结束线程----
- 原理: 线程可以调用wait()方法,让当前的线程处于钝化的状态(会立即释放CPU,并无法强CPU的状态,但当前的线程并没有死亡)
- 调用interrupt方法就是将处于wait状态下的线程停止
- 注意:wait方法必须在同步状态下使用
public class Demo7 {
public static void main(String[] args) {
Test1 test1 = new Test1();
Thread thread = new Thread(test1);
thread.start();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
int i = 0;
while(true) {
if(++i == 1000000000) {
thread.interrupt();
break;
}
}
}
}
class Test1 implements Runnable{
boolean flag = true;
@Override
public synchronized void run() {
while(flag) {
try {
wait();
} catch (InterruptedException e) {
flag = false;
System.out.println("InterruptedException");
}
System.out.println(Thread.currentThread().getName()+" 我们很嗨皮");
}
}
}