package(包)
1.包的书写格式就是: package 包的名称。
2.import:关键字是可以导入包中的类,不能导入包中包。目的是为了简化类名书写。
package mypack;
import packA.*;//导入packA包中所有的类,不导入包中的包.
import packA.DemoA;//导入packA中的DemoA类
//导包的原则:用到那个类,就导入那个类
import //干嘛的用,为了简化类名书写
class PackageDemo// extends packA.DemoA
{
public static void main(String[] args)
{
DemoA a = new DemoA();//导包后这样写
// packA.DemoA a = new packA.DemoA();//没有导包这样写
}
}
3.包与包之间的类进行访问时,被访问的包中的类必须是public的,被访问的包中的类的方法也必须是public的.
4.protected:被该修饰符修饰的方法只能由子类使用,其他类创建该类的对象也无法使用.
thread(线程)
1.1首先要明确进程的概念
就是正在进行中的程序,有PCB,数据段,程序段组成.是系统资源分配的单元.
1.2.线程
就是进程中的一个负责程序执行的控制单元(执行单元)
1.3 一个进程中可以建立多个线程,一个进程中至少有一个线程。
开启多个线程是为了让多个线程执行多个任务或者执行一个任务,这样可以让多个程序代码在一个时间段内并发执行,但是CPU(针对单核)在某一个时刻,只执行一个线程。宏观上同时执行,微观上轮流执行。开启多线程的目的是开启另一条路径让指定的代码任务和其他代码任务同时执行。虽然说开启多线程可以让多个代码任务同时执行,但是这样做降低了效率。
1.4 main方法里的是主线程,是系统默认创建的。那我们如何自定义线程任务呢?
这里有两种方式,一种是继承Thread类,成为Thread的子类,重写run方法,其中run方法是就是封装自定义线程运行任务的函数,run方法中定义的就是线程要运行的任务代码。说白了就是你想让指定的任务进行多线程执行,就把这个任务放入run方法中。另一种是实现 Runable 接口,重写run方法。
创建线程方式之一:继承Thread类
步骤:
(1)定义一个类继承Thread类。
(2)覆盖Thread中run方法。
(3)直接创建Thread的子类对象创建线程。
(4)调用start方法开启线程,它会自动调用run方法。
class Demo extends Thread
{
public void run()
{
//......任务代码
}
}
class Test
{
public static void main(String[] args)
{
Demo d = new Demo();
d.start();
}
}
方式之二:实现 Runnable 接口
(1)定义类实现Runnable接口。
(2)覆盖run方法,将线程的任务代码封装在run方法中。
(3)通过Thread类创建线程对象,并将Runnable接口的子类作为Thread类的构造函数的参数进行传递。
(4)调用线程对象的start方法开启线程。
class Demo implements Runnable
{
public void run()
{
//......任务代码
}
}
class Test
{
public static void main(String[] args)
{
Demo d = new Demo();
Thread t = new Thread(d);
t.start();
}
}
建议使用第二种方式,因为它更方便灵活,不用担心造成多继承的问题,
而且它只是对指定任务的封装,当一个对象创建多个线程,对象中的数据多个线程是可以共享的,下面例子中的num就是共享数据,3个线程对一个num = 100 进行操作。当然它也可以对3个num进行操作,只需要创建三个对象,把指定任务传入线程即可。
class Demo implements Runnable
{
private int num = 100;
public void run()
{
//......任务代码
}
}
class Test
{
public static void main(String[] args)
{
Demo d = new Demo();
Thread t1 = new Thread(d);
Thread t2 = new Thread(d);
Thread t3= new Thread(d);
t1.start();
t2.start();
t3.start();
}
}
5.当一个线程在执行操作共享数据的多条代码中,其他线程参与了运算,就会导致线程安全问题的产生。
解决办法,将多条操作共享数据的线程代码封装起来,当有线程在执行这些代码的时候,其他线程是不可以参与运算的,必须要当前线程把这些代码都执行完毕后,其他线程才可以参与运算。
比如火车上的厕所没有锁,如果一个人正在使用,其他人这时也要使用,就会破门而入,这样就是导致问题的产生。所以要加一把锁,当这个人在使用的过程中,其他人无法开门使用,就算这个人在厕所中睡着了,也必须等这个人使用完毕,其他人才可以进入。
所以就引入了同步锁,
同步代码块
synchronize(对象)
{
需要被同步的代码;
}
同步函数
public synchronize void test(){}
同步代码块的锁是任意的,而同步函数的锁是this,静态同步函数的锁是该类的类名.class。
同步是有前提的,多个线程必须使用同 一把锁,虽然同步锁解决了线程的安全问题但是相对的降低了效率,因为同步外的线程都会判断同步锁。
6.多线程下的单例模式
饿汉式:
class Single
{
private static final Single s = new Single();
private Single(){}
public static synchronized Single getInstance()
{
return s;
}
}
懒汉式:
class Single
{
private static Single s;
private Single(){}
public static /*synchronized*/Single getInstance()
{
if (s == null)//加判断是为了提高效率,减少每个线程判断同步锁是否被其他线程持有的次数
{
synchronized (Single.class) {//加同步锁是为了保证线程的安全
if (s == null) {
s = new Single();
}
}
}
return s;
}
}
7 虽然同步锁解决了多线程对共享数据进行操作访问时的安全问题,但是同步锁的嵌套又会引发死锁的问题。
常见情景之一: 同步的嵌套
package Thread_study;
class Mylock
{
public static Mylock lockA = new Mylock();
public static Mylock lockB = new Mylock();
}
class Test implements Runnable
{
boolean flag ;
Test(boolean flag)
{
this.flag = flag;
}
public void method()
{
if(flag)
{
while(true)
synchronized (Mylock.lockA)
{
System.out.println("if......lockA");
synchronized (Mylock.lockB)
{
System.out.println("if......lockB");
}
}
}
else
{
while (true)
synchronized (Mylock.lockB)
{
System.out.println("else.......lockB");
synchronized (Mylock.lockA)
{
System.out.println("else.......lockA");
}
}
}
}
public void run()
{
this.method();
}
}
public class DeadLockDemo4
{
public static void main(String[] args)
{
Test p1 = new Test(true);
// Test p2 = new Test(false);
Thread t1 = new Thread(p1);
Thread t2 = new Thread(p1);
t1.start();
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
p1.flag = false;
t2.start();
}
}
8.本周遇到的问题:
为了看到多线程运行的现象,在一些代码块上加了while(true),导致线程一直在运行,自己以为结束了,然后就再次点击了运行,最后CPU满负荷运行,最终导致编译器非常的卡。“Process finished with exit code 1”这句话的出现才标志程序的结束。